Práctica: Los Juegos del Hambre¶
ESTUDIANTE: MANUELA LARREA, JERSON PEÑA
![]() |
![]() |
![]() |
En esta práctica vamos a enfrentarnos a un problema desafiante de clasificación de imágenes, construyendo una red neuronal profunda que sea capaz de clasificar entre diferentes tipos de comida. ¡Que comiencen los Juegos del Hambre!
Guidelines¶
A lo largo del notebook encontrarás celdas que debes rellenar con tu propio código. Sigue las instrucciones del notebook y presta atención a los siguientes iconos:

Deberás resolver el ejercicio escribiendo tu propio código o respuesta en la celda inmediatamente inferior.

Esto es una pista u observación de utilidad que puede ayudarte a resolver el ejercicio. Presta atención a estas pistas para comprender el ejercicio en mayor profundidad.

Este es un ejercicio avanzado que te puede ayudar a profundizar en el tema, y a conseguir una calificación más alta. ¡Buena suerte!
Para evitar problemas con imports o incompatibilidades se recomienda hacer uso Google Colaboratory. Asegúrate de conectar una GPU, y de haber deactivado otras sesiones que tuvieras activas.
El siguiente código mostrará todas las gráficas en el propio notebook en lugar de generar una nueva ventana.
# Importaciones
import os
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import pathlib
from random import sample
from PIL import Image
from tensorflow.random import set_seed
from utils import helper # Modulo implementado
from utils import constants # Modulo implementado
np.random.seed(constants.SEED)
set_seed(constants.SEED)
Obtención de datos¶
Vamos a usar un dataset de imágenes de comida disponible en Kaggle.

Usa la celda inferior para descargar el dataset de Kaggle. ¡Ojo! Cuando entregues este notebook no dejes en él tus credenciales, ya que son personales a tu usuario de Kaggle.
os.environ["KAGGLE_USERNAME"] = "albarji"
os.environ["KAGGLE_KEY"] = "c98bec07523d01caaa7f117668cf22ce"
!kaggle datasets download trolukovich/food11-image-dataset --unzip
Dataset URL: https://www.kaggle.com/datasets/trolukovich/food11-image-dataset License(s): CC0-1.0 food11-image-dataset.zip: Skipping, found more recently modified local copy (use --force to force download)
Revisa ahora la carpeta en la que has descargado los datos. Verás que contiene 3 subdirectorios:
- training, contiene las imágenes a utilizar para entrenar el modelo.
- validation, contiene imágenes adicionales que podrías usar como datos de entrenamiento adicionales, o para algún tipo de estrategia de validación como Early Stopping.
- evaluation, contiene las imágenes que debes utilizar para testear el modelo. Las imágenes de esta carpeta solo pueden utilizarse para medir el rendimiento del modelo tras su entrenamiento, y para nada más.
Además de esto, dentro de cada una de estas carpetas encontrarás una subcarpeta para cada una de las 11 clases de comida:
- Bread (panes)
- Dairy product (lácteos)
- Dessert (postres)
- Egg (huevos)
- Fried food (fritos)
- Meat (carnes)
- Noodles-Pasta (pasta)
- Rice (arroz)
- Seafood (pescado y marisco)
- Soup (sopas)
- Vegetable-Fruit (vegetales y frutas)
Para facilitar los pasos de procesamiento que vendrán a continuación, vamos a definir algunas variables que nos indiquen dónde están almacenados los diferentes conjuntos de datos.

Crea variables TRAINDIR, VALDIR y TESTDIR, cada una conteniendo una cadena de texto con la ruta al directorio donde están los datos de entrenamiento, validación y evaluación, respectivamente.
TRAINDIR, VALDIR, TESTDIR = 'training','validation', 'evaluation'
Reducción de clases¶
Con el fin de hacer este problema más accesible de cara a la práctica, vamos a centrarnos solo en seis de las clases de comida disponibles: Bread, Dairy product, Dessert, Egg, Fried food y Meat. Para ello, se provee el código siguiente, que elimina de los datos descargados las carpetas correspondientes a imágenes de las otras clases.
# Adecuo la función para correr el proyecto en windows local.
valid_classes = {"Bread", "Dairy product", "Dessert", "Egg", "Fried food", "Meat"}
datasets = {TRAINDIR, VALDIR, TESTDIR}
for dataset in datasets:
for classdir in pathlib.Path(dataset).glob("*"): # Find subfolders with classes
class_name = classdir.name
if class_name not in valid_classes: # Ignore those in valid_classes
print(f"Deleting {classdir}...")
for fname in classdir.glob("*.jpg"): # Remove each image file
fname.unlink()
classdir.rmdir() # Remove folder
Carga de datos¶

Utiliza la función image_dataset_from_directory para crear tres objetos de tipo Dataset, uno para cada una de las particiones de datos (training, validation, evaluation)

¿Cuál es el tamaño ideal al que cargar las imágenes en memoria? Esto es algo con lo que tendrás que experimentar a lo largo del notebook, por lo que probablemente tendrás que volver más de una vez a esta celda y probar cambiando ese tamaño.
Observemos como son las imagenes y sus tamaños
train = {}
for i in os.listdir(TRAINDIR):
train[i] = os.listdir(os.path.join(TRAINDIR, i))
fig,axs = plt.subplots(len(train.keys()), 5, figsize=(15,15))
for i, item in enumerate(os.listdir(TRAINDIR)):
images = sample(train[item], 5)
for j, image in enumerate(images):
img = Image.open(os.path.join(TRAINDIR, item, image))
axs[i,j].imshow(img)
axs[i,j].set(xlabel = item, xticks = [], yticks = [])
fig.tight_layout()
Preprocesamiento¶
Lo más flexible aquí es tener una función lo suficientemente modular para poder experimentar con el tamaño de las imagenes (image_size) y el tamaño del lote (batch_size).
Iniciamos con un experimento que toma tamaños de imagen de 32x32 y un lote de 64. Estos tamaños son los suficientemente pequeños para tener un procesamiento "respetable", y lo suficientemente grandes como para poder capturar características importantes de las imágenes.
train_dataset = helper.create_dataset(TRAINDIR)
Found 6082 files belonging to 6 classes.
Exploremos las características del dataset.
helper.get_batch_info(train_dataset)
Tamaño del batch de entrada: (32, 32, 32, 3) Tamaño del batch de salida: (32, 6) Batch de entrada: [[[[127.28906 122.28906 118.78906 ] [133.92969 131.42969 129.92969 ] [139.92969 134.92969 131.42969 ] ... [102.92969 70.07031 51. ] [120.71094 92.71094 78.71094 ] [142.35938 119.359375 105.359375]] [[133.13281 129.13281 126.13281 ] [138.28906 133.78906 130.78906 ] [142.78906 137.78906 134.78906 ] ... [106.39844 71. 50.132812] [ 91.36719 60.476562 44.390625] [122.86719 99.13281 87.265625]] [[137.64844 132.64844 129.64844 ] [141.64844 137.14844 134.14844 ] [145.70312 137.70312 134.70312 ] ... [ 88.55469 64.5 47.445312] [ 48.90625 31.203125 21.5 ] [ 85.890625 61.351562 48.203125]] ... [[126.35156 112.35156 112.35156 ] [189.05469 210.64844 245.35156 ] [171.64844 196.64844 226.64844 ] ... [164.85156 165.70312 171.14844 ] [161.5 162.5 167.5 ] [159.79688 158.79688 164.79688 ]] [[129.8125 110.8125 106.390625] [213.84375 229.84375 254.42188 ] [171.28906 194.28906 225.28906 ] ... [162.21094 163.21094 168.21094 ] [160.21094 159.21094 165.21094 ] [156.78906 155.78906 161.78906 ]] [[131.5 111.359375 112.92969 ] [148.375 111.65625 106.21094 ] [180.07031 200.07031 234.21094 ] ... [161.5 158.5 165.5 ] [158.5 157.5 163.5 ] [157.57031 155.57031 160.57031 ]]] [[[ 28.5 17. 16. ] [ 29.75 19.75 18.25 ] [ 30.75 18.75 18.75 ] ... [107.25 161. 216.25 ] [117.75 185.25 243.75 ] [145.75 218. 253.75 ]] [[ 38.25 27. 26.25 ] [ 33.75 21.75 22.25 ] [ 34.75 22.25 20.75 ] ... [ 94.5 133.5 179.5 ] [ 93.75 153.25 217.75 ] [ 48.75 94.75 138.25 ]] [[ 30.25 22.75 23.25 ] [ 35.5 26.5 27.5 ] [ 41.5 27. 24. ] ... [ 37.25 40.5 54.75 ] [ 33. 50. 79. ] [ 36.75 57.5 96.75 ]] ... [[ 65.25 83.75 114.25 ] [ 58.5 76.5 100.25 ] [104.75 105.5 81.25 ] ... [144.5 187.5 231.5 ] [145.75 190.5 233.25 ] [144. 184.5 237.5 ]] [[ 75.25 104.75 144.5 ] [ 61.25 84.5 117.5 ] [ 57.5 76.5 107.5 ] ... [143. 183.5 225. ] [135.5 185. 232.5 ] [134.75 184.25 233. ]] [[126.5 168.5 214. ] [104. 136.75 181. ] [ 73. 101.5 148.75 ] ... [147. 187.25 231.5 ] [144.75 189.25 231.5 ] [154.75 203.75 244. ]]] [[[137. 129. 142. ] [133.5 105.25 108. ] [136.5 97. 90.25 ] ... [176. 132. 126. ] [169.5 127.5 122. ] [153.5 119.5 119.5 ]] [[144. 128. 140. ] [132.5 107. 110.5 ] [134.5 97.25 94. ] ... [190.25 150.75 151.75 ] [189.25 160.5 164.5 ] [189.5 162.75 169.75 ]] [[139. 127.5 135.75 ] [136.25 108.75 111.25 ] [135. 100.5 97.5 ] ... [221. 208.25 211.5 ] [206.75 207.75 205.25 ] [205. 206. 203.5 ]] ... [[ 6.5 6.5 8.5 ] [ 6.75 6.75 8.75 ] [ 10. 10. 12. ] ... [126. 86. 84. ] [128. 88. 86. ] [129. 90. 85. ]] [[ 6.25 6.25 8.25 ] [ 7. 5. 8. ] [ 8. 8. 8. ] ... [123.5 82.5 76.5 ] [122. 82. 82. ] [126. 85. 82. ]] [[ 5.25 5.25 5.25 ] [ 5.25 5.25 5.25 ] [ 7. 7. 7. ] ... [125.25 81.25 71.75 ] [120. 82. 74.5 ] [115.5 81.25 78. ]]] ... [[[165. 174.75 174.75 ] [119. 121. 125.5 ] [129. 134. 139. ] ... [ 76.5 68.5 62.5 ] [124.75 126.5 127. ] [210. 227.5 234.25 ]] [[ 74. 69. 74.75 ] [ 99. 104.25 106.75 ] [108.25 111.75 119.75 ] ... [ 56. 52. 43. ] [175. 186. 191. ] [ 96.5 111. 119.75 ]] [[120.25 125.5 136.75 ] [248.5 253.5 255. ] [251.25 255. 254.25 ] ... [ 73.75 71.75 74. ] [ 59.5 56.75 51.5 ] [ 65.25 65.75 62.25 ]] ... [[235. 246. 239.5 ] [236.5 235. 226.5 ] [114.75 88.75 64. ] ... [233.75 244.75 236.75 ] [243.75 254.75 250.75 ] [229.75 244.75 239.75 ]] [[235.5 245.5 236.5 ] [233. 241.5 234. ] [129. 126.25 118.75 ] ... [233.25 244.25 236.25 ] [231.25 244.25 237.25 ] [227.25 242.25 236.25 ]] [[235. 245. 237. ] [236.25 246.25 238.25 ] [219.5 228.5 216.5 ] ... [232.25 243.25 235.25 ] [232. 245. 236. ] [229.5 244.5 237.5 ]]] [[[167.75 154. 128.75 ] [221. 221.5 208.5 ] [176.5 163.5 138. ] ... [171.5 107.5 36.5 ] [162. 102. 29.5 ] [167.5 104.5 37.5 ]] [[171.5 120.5 55. ] [114.5 74.75 18. ] [ 98.25 59.75 3.75 ] ... [174.75 112.75 39.75 ] [171.5 110.5 44. ] [166.75 105.25 35.75 ]] [[192. 138. 70.5 ] [175.25 120.25 53.25 ] [172.5 126.5 81. ] ... [172.75 110.75 37.75 ] [173.25 113.5 39.75 ] [168.5 109.5 39.5 ]] ... [[167.25 128.25 71.25 ] [166.5 128.75 68. ] [180.25 140.25 79.25 ] ... [181.5 143.5 81.5 ] [181.5 141.5 80.5 ] [181.25 141.25 81.75 ]] [[167. 131. 77.5 ] [150.75 118.75 65.25 ] [147.5 116.5 62. ] ... [181. 142. 85. ] [179.75 138.75 82.75 ] [182.5 146.25 89. ]] [[153.75 119.75 72.75 ] [153.75 123.75 71.75 ] [143. 113. 62. ] ... [171.25 137.25 83.75 ] [177.25 141.75 88.75 ] [175.75 141. 87.75 ]]] [[[182.75 149.75 140.75 ] [169.75 141.75 133.25 ] [132.75 111.25 103.75 ] ... [ 33. 29. 19. ] [ 34.5 31.5 25.5 ] [ 28.5 25.5 19.5 ]] [[181.75 148.75 136.25 ] [166. 135. 124.5 ] [127.25 102.75 98.5 ] ... [ 35. 26. 17. ] [ 34.5 27.5 18.5 ] [ 36.75 32. 23.25 ]] [[169.5 131.75 114. ] [157. 125. 114. ] [119.75 95.75 89.25 ] ... [ 40.5 30.5 18.5 ] [ 41.5 31.5 19.5 ] [ 39.75 31.75 18.75 ]] ... [[139. 125.5 113.25 ] [146.5 129.5 111. ] [167. 151. 130.5 ] ... [123.5 92.5 63.5 ] [129.25 98.25 67.25 ] [146.25 113.25 81.25 ]] [[141. 121.25 102.5 ] [151.75 133.75 112.75 ] [152.75 134.75 111.25 ] ... [123.75 92. 63.25 ] [119.75 88.5 61.25 ] [133. 103. 72.5 ]] [[ 88.5 48.5 18.5 ] [133.25 111.25 86.5 ] [162.25 144. 119.25 ] ... [139.5 110.5 79.5 ] [118.5 93.5 68. ] [143.25 117.75 89.25 ]]]] Batch de salida: [[0. 0. 0. 1. 0. 0.] [0. 0. 1. 0. 0. 0.] [0. 0. 0. 0. 1. 0.] [0. 0. 0. 0. 1. 0.] [0. 0. 0. 0. 1. 0.] [0. 0. 0. 0. 0. 1.] [1. 0. 0. 0. 0. 0.] [0. 0. 1. 0. 0. 0.] [0. 0. 0. 0. 0. 1.] [1. 0. 0. 0. 0. 0.] [0. 0. 0. 1. 0. 0.] [0. 1. 0. 0. 0. 0.] [1. 0. 0. 0. 0. 0.] [1. 0. 0. 0. 0. 0.] [0. 0. 1. 0. 0. 0.] [0. 0. 0. 0. 0. 1.] [0. 0. 0. 0. 1. 0.] [1. 0. 0. 0. 0. 0.] [0. 0. 1. 0. 0. 0.] [0. 0. 0. 0. 1. 0.] [0. 0. 0. 0. 1. 0.] [0. 0. 0. 0. 0. 1.] [0. 0. 0. 0. 1. 0.] [1. 0. 0. 0. 0. 0.] [0. 0. 0. 0. 1. 0.] [0. 0. 0. 1. 0. 0.] [0. 0. 0. 1. 0. 0.] [0. 0. 1. 0. 0. 0.] [1. 0. 0. 0. 0. 0.] [0. 0. 1. 0. 0. 0.] [0. 0. 1. 0. 0. 0.] [0. 0. 1. 0. 0. 0.]]
La salida es clara:
- Las imagenes quedaron correctamente re-escaladas a un tamaño de 32X32px.
- El tamaño del lote es 64
- Las salidas quedaron codificadas de forma binaria (One-Hot)
- El número de canales es 3 (RGB). Esto significa que cada píxel se encuentra en el rango [0,255], por la formula: ${2}^8-1$. Esto puede generar problemas a la hora del entrenamiento. No obstante, resolveremos esto más adelante.
Podemos proseguir, entonces, creando el resto de datasets.
val_dataset = helper.create_dataset(VALDIR)
test_dataset = helper.create_dataset(TESTDIR)
Found 2108 files belonging to 6 classes. Found 2070 files belonging to 6 classes.
helper.get_batch_info(val_dataset, full_view = False)
Tamaño del batch de entrada: (32, 32, 32, 3) Tamaño del batch de salida: (32, 6)
helper.get_batch_info(test_dataset, full_view = False)
Tamaño del batch de entrada: (32, 32, 32, 3) Tamaño del batch de salida: (32, 6)
Se conservan las características del dataset de train
Construyendo tu propia red¶

Diseña una red neuronal profunda que obtenga el mejor acierto posible sobre los datos de test. Puedes usar los datos de entrenamiento y validación como te parezca mejor, pero sólo puedes usar los datos de test para evaluar el acierto de tu modelo. Debes obtener una red capaz de alcanzar al menos un 45% de acierto sobre los datos de test.

Algunas recomendaciones y estrategias que pueden ayudar a mejorar tu diseño de red:
- Arquitectura: usa todos los trucos que has aprendido en los ejercicios anteriores: capas convolucionales + pooling, activaciones ReLU, dropout... asegúrate también de utilizar un buen optimizador, con una función de error (loss) adecuada, así como una función de activación en la capa de salida que sea adecuada para esta clase de problema (clasificación multiclase).
- Desarrollo incremental: empieza por redes pequeñas, con un número pequeño de parámetros, de forma que puedas comprobar rápidamente qué tal funcionan. Después, puedes hacer tu red más grande en tres direcciones: mayor tamaño de imágenes de entrada, más capas, y más kernels por capa convolucional o unidades por capa densa. Si aumentas el tamaño de las imágenes de entrada, asegúrate de añadir también más capas Convolution+Pooling, para que así a la capa Flatten solo lleguen imágenes muy pequeñas (10x10 píxeles o menos).
- Tamaño de imágenes: configurar los
Datasetpara que carguen imágenes de mayor tamaño puede mejorar significativamente el rendimiento de tu red. Pero ten cuidado, ¡también puedes encontrarte errores de falta de memoria (CUDA memory error) si cargas imágenes a un tamaño demasiado grande! Para esta práctica, un tamaño mayor a 256 puede ser demasiado grande... - Controlar el número de épocas: Usa una estrategia de EarlyStopping para monitorizar el loss de los datos de validación, y así detener el entrenamiento cuando tras un número de épocas esa loss no haya decrecido. Configurar la EarlyStopping para restaurar los mejores parámetros encontrados durante la optimización también puede resultarte útil.
- Sobreajuste: si tu red obtiene un accuracy casi perfecto en entrenamiento, puede que estés sufriendo sobreajuste (aunque puede que no...). Prueba a incrementar el nivel de Dropout en las capas Dense para comprobar si así obtienes mejoras en el test.
- Demasiado bueno para ser verdad: si tu red obtiene resultados muy buenos, del orden del 90% o más de acierto en test... sospecha. Es posible que estés mezclando los datos de entrenamiento y test.

Como ejercicio avanzado, añade las siguiente estrategias a tu red:
- Usa técnicas de "image augmentation" para aumentar artificialmente tu dataset de entrenamiento. Para ello, explora las capas de augmentation disponibles en Keras.
- Usa capas de BatchNormalization para facilitar el entrenamiento de la red. Revisa en las diapositivas de clase cuál es la forma adecuada de colocarlas en la red.
Usando estos trucos y los mencionados en el punto anterior, es posible obtener más de un 60% de acierto en el conjunto de test.
loss_list = []
accuracy_list = []
Modelo 1¶
Iniciamos con un modelo muy básico. El optimizador utilizado es 'sgd', que es un optimizador de gradiente estocástico simple.
El modelo consta de las siguientes capas:
Capa de escalado (Rescaling)
Capa de convolución (Conv2D)
Capa de aplanamiento (Flatten)
Capa completamente conectada (Dense)
La función de pérdida utilizada es 'categorical_crossentropy', que es adecuada para problemas de clasificación multiclase.
modelo_1 = helper.create_model()
c:\Python312\Lib\site-packages\keras\src\layers\preprocessing\tf_data_layer.py:18: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead. super().__init__(**kwargs)
modelo_1.fit(train_dataset, epochs=1)
191/191 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - accuracy: 0.2577 - loss: 1.7346
<keras.src.callbacks.history.History at 0x1f64cf16c60>
loss_1, acc_1 = modelo_1.evaluate(test_dataset)
print(f"Loss {loss_1:.3}, accuracy {acc_1:.1%}")
65/65 ━━━━━━━━━━━━━━━━━━━━ 1s 7ms/step - accuracy: 0.2756 - loss: 1.6844 Loss 1.68, accuracy 28.5%
loss_list.append(loss_1)
accuracy_list.append(acc_1)
Modelo 2¶
Probamos con el optimizador 'adam', que es un optimizador de gradiente estocástico adaptativo.
modelo_2 = helper.create_model(optimizer = 'adam')
modelo_2.fit(train_dataset, epochs=1, validation_data=val_dataset)
191/191 ━━━━━━━━━━━━━━━━━━━━ 2s 8ms/step - accuracy: 0.2715 - loss: 1.7503 - val_accuracy: 0.3563 - val_loss: 1.5630
<keras.src.callbacks.history.History at 0x1f64d449370>
loss_2, acc_2 = modelo_2.evaluate(test_dataset)
print(f"Loss {loss_2:.3}, accuracy {acc_2:.1%}")
loss_list.append(loss_2)
accuracy_list.append(acc_2)
65/65 ━━━━━━━━━━━━━━━━━━━━ 0s 7ms/step - accuracy: 0.3399 - loss: 1.6075 Loss 1.59, accuracy 34.0%
Modelo 3¶
Para hacer el modelo más robusto, agregamos una capa de Pooling después de la capa de convolución para reducir la dimensionalidad de las características extraídas
Respecto al modelo anterior, se le incrementa:
- Capa de escalado (Rescaling)
- Capa de convolución (Conv2D)
- Capa de pooling
- Capa de aplanamiento
- Capa completamente conectada
modelo_3 = helper.create_model_3()
modelo_3.fit(train_dataset, epochs=10, validation_data=val_dataset)
Epoch 1/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 2s 8ms/step - accuracy: 0.2374 - loss: 1.7452 - val_accuracy: 0.3368 - val_loss: 1.5825 Epoch 2/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 1s 7ms/step - accuracy: 0.3484 - loss: 1.5541 - val_accuracy: 0.3838 - val_loss: 1.5007 Epoch 3/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 1s 7ms/step - accuracy: 0.4025 - loss: 1.4722 - val_accuracy: 0.3890 - val_loss: 1.4705 Epoch 4/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 1s 7ms/step - accuracy: 0.4194 - loss: 1.4285 - val_accuracy: 0.3971 - val_loss: 1.4515 Epoch 5/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.4274 - loss: 1.4058 - val_accuracy: 0.3961 - val_loss: 1.4452 Epoch 6/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 1s 7ms/step - accuracy: 0.4399 - loss: 1.3838 - val_accuracy: 0.4094 - val_loss: 1.4439 Epoch 7/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 1s 7ms/step - accuracy: 0.4516 - loss: 1.3629 - val_accuracy: 0.3999 - val_loss: 1.4481 Epoch 8/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 1s 7ms/step - accuracy: 0.4578 - loss: 1.3533 - val_accuracy: 0.4065 - val_loss: 1.4458 Epoch 9/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 1s 7ms/step - accuracy: 0.4669 - loss: 1.3358 - val_accuracy: 0.4070 - val_loss: 1.4491 Epoch 10/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 1s 7ms/step - accuracy: 0.4800 - loss: 1.3233 - val_accuracy: 0.4028 - val_loss: 1.4559
<keras.src.callbacks.history.History at 0x1f64eeada60>
loss_3, acc_3 = modelo_3.evaluate(test_dataset)
print(f"Loss {loss_3:.3}, accuracy {acc_3:.1%}")
loss_list.append(loss_3)
accuracy_list.append(acc_3)
65/65 ━━━━━━━━━━━━━━━━━━━━ 0s 7ms/step - accuracy: 0.3918 - loss: 1.4805 Loss 1.46, accuracy 39.9%
Modelo 4¶
Para hacer el modelo aún más robusto, podemos aumentar el número de capas convolucionales y densas, lo que permite aprender características más complejas de las imágenes.
Respecto al modelo anterior, se le incrementa:
Capa de escalado
Capa de convolución
Capa de pooling: reduce la dimensionalidad de la salida de la capa de convolución para reducir la cantidad de parámetros.
Capa de convolucióng: aplica otro filtro de convolución a la imagen de entrada para extraer características más complejas.
Capa de pooling: reduce la dimensionalidad de la salida de la capa de convolución para reducir la cantidad de parámetros.
Capa de aplanamiento: aplana la salida de la capa de pooling para prepararla para la capa densa.
Capa completamente conectada: clasifica las características aplanadas en una de las clases posibles.
Capa de dropout: aplica regularización para evitar sobreajuste.
modelo_4 = helper.create_model_4()
c:\Python312\Lib\site-packages\keras\src\layers\convolutional\base_conv.py:107: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead. super().__init__(activity_regularizer=activity_regularizer, **kwargs)
modelo_4.fit(train_dataset, epochs=10, validation_data=val_dataset)
Epoch 1/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 12ms/step - accuracy: 0.2191 - loss: 5.8620 - val_accuracy: 0.3046 - val_loss: 1.6634 Epoch 2/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.2640 - loss: 1.7208 - val_accuracy: 0.3202 - val_loss: 1.6362 Epoch 3/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.2897 - loss: 1.6505 - val_accuracy: 0.3425 - val_loss: 1.5581 Epoch 4/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.3164 - loss: 1.6024 - val_accuracy: 0.4065 - val_loss: 1.5291 Epoch 5/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.3385 - loss: 1.5695 - val_accuracy: 0.4089 - val_loss: 1.4871 Epoch 6/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.3623 - loss: 1.5278 - val_accuracy: 0.4179 - val_loss: 1.4443 Epoch 7/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.3864 - loss: 1.4857 - val_accuracy: 0.4042 - val_loss: 1.4434 Epoch 8/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.3872 - loss: 1.4990 - val_accuracy: 0.4421 - val_loss: 1.4133 Epoch 9/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.3831 - loss: 1.4763 - val_accuracy: 0.3449 - val_loss: 1.5511 Epoch 10/10 191/191 ━━━━━━━━━━━━━━━━━━━━ 2s 11ms/step - accuracy: 0.3991 - loss: 1.4748 - val_accuracy: 0.4478 - val_loss: 1.4016
<keras.src.callbacks.history.History at 0x1f64eed5430>
loss_4, acc_4 = modelo_4.evaluate(test_dataset)
print(f"Loss {loss_4:.3}, accuracy {acc_4:.1%}")
loss_list.append(loss_4)
accuracy_list.append(acc_4)
65/65 ━━━━━━━━━━━━━━━━━━━━ 1s 8ms/step - accuracy: 0.4240 - loss: 1.4435 Loss 1.42, accuracy 43.7%
Modelo 5¶
En este modelo implemento los mismos parámetros de entrada del modelo 4, pero se le incrementa:
- Aumentación de datos con ImageDataGenerator, con variaciones en: rotation, shift, shear, zoom, and flip.
- Parada de entrenamiento con EarlyStopping para la métrica de accuracy
datagen = helper.create_image_augmentor()
early_stopping = helper.create_early_stopping()
modelo_5 = helper.create_model_4()
train_images, train_labels = helper.extract_numpy_matrices(train_dataset)
history=modelo_5.fit(
datagen.flow(train_images, train_labels),
epochs = 50,
validation_data=val_dataset,
callbacks=[early_stopping]
)
Epoch 1/50 7/191 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.1449 - loss: 39.7625
c:\Python312\Lib\site-packages\keras\src\trainers\data_adapters\py_dataset_adapter.py:121: UserWarning: Your `PyDataset` class should call `super().__init__(**kwargs)` in its constructor. `**kwargs` can include `workers`, `use_multiprocessing`, `max_queue_size`. Do not pass these arguments to `fit()`, as they will be ignored. self._warn_if_super_not_called()
191/191 ━━━━━━━━━━━━━━━━━━━━ 5s 15ms/step - accuracy: 0.2016 - loss: 7.0015 - val_accuracy: 0.2576 - val_loss: 1.6895 Epoch 2/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.2639 - loss: 1.7169 - val_accuracy: 0.3477 - val_loss: 1.5816 Epoch 3/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 4s 18ms/step - accuracy: 0.2753 - loss: 1.6712 - val_accuracy: 0.3567 - val_loss: 1.5588 Epoch 4/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 4s 20ms/step - accuracy: 0.2996 - loss: 1.6349 - val_accuracy: 0.3771 - val_loss: 1.5121 Epoch 5/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.3254 - loss: 1.6033 - val_accuracy: 0.3544 - val_loss: 1.5274 Epoch 6/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 13ms/step - accuracy: 0.3456 - loss: 1.5768 - val_accuracy: 0.3843 - val_loss: 1.4977 Epoch 7/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 4s 20ms/step - accuracy: 0.3377 - loss: 1.5864 - val_accuracy: 0.3828 - val_loss: 1.5189 Epoch 8/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 4s 20ms/step - accuracy: 0.3519 - loss: 1.5340 - val_accuracy: 0.3767 - val_loss: 1.5097 Epoch 9/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.3599 - loss: 1.5492 - val_accuracy: 0.3871 - val_loss: 1.5048 Epoch 10/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 4s 20ms/step - accuracy: 0.3747 - loss: 1.5190 - val_accuracy: 0.3838 - val_loss: 1.5428 Epoch 11/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 16ms/step - accuracy: 0.3750 - loss: 1.5310 - val_accuracy: 0.4051 - val_loss: 1.4250 Epoch 12/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.3804 - loss: 1.5270 - val_accuracy: 0.4094 - val_loss: 1.4973 Epoch 13/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 16ms/step - accuracy: 0.3670 - loss: 1.5543 - val_accuracy: 0.4198 - val_loss: 1.4060 Epoch 14/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.3980 - loss: 1.5017 - val_accuracy: 0.4364 - val_loss: 1.4100 Epoch 15/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 16ms/step - accuracy: 0.3922 - loss: 1.5086 - val_accuracy: 0.4004 - val_loss: 1.4804 Epoch 16/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 16ms/step - accuracy: 0.3862 - loss: 1.5093 - val_accuracy: 0.4435 - val_loss: 1.4205 Epoch 17/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.4030 - loss: 1.4822 - val_accuracy: 0.4611 - val_loss: 1.3732 Epoch 18/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 13ms/step - accuracy: 0.3990 - loss: 1.4746 - val_accuracy: 0.4492 - val_loss: 1.3737 Epoch 19/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 4s 19ms/step - accuracy: 0.4330 - loss: 1.4478 - val_accuracy: 0.4734 - val_loss: 1.3376 Epoch 20/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 4s 20ms/step - accuracy: 0.4264 - loss: 1.4360 - val_accuracy: 0.4236 - val_loss: 1.4353 Epoch 21/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 16ms/step - accuracy: 0.4346 - loss: 1.4440 - val_accuracy: 0.4507 - val_loss: 1.4079 Epoch 22/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 15ms/step - accuracy: 0.4131 - loss: 1.4569 - val_accuracy: 0.4440 - val_loss: 1.3756 Epoch 23/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.4281 - loss: 1.4907 - val_accuracy: 0.4663 - val_loss: 1.3402 Epoch 24/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 16ms/step - accuracy: 0.4343 - loss: 1.4364 - val_accuracy: 0.4734 - val_loss: 1.3262 Epoch 25/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 15ms/step - accuracy: 0.4298 - loss: 1.4254 - val_accuracy: 0.4749 - val_loss: 1.3596 Epoch 26/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 4s 19ms/step - accuracy: 0.4471 - loss: 1.4125 - val_accuracy: 0.4530 - val_loss: 1.3961 Epoch 27/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 15ms/step - accuracy: 0.4303 - loss: 1.4266 - val_accuracy: 0.4592 - val_loss: 1.3968 Epoch 28/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.4344 - loss: 1.4226 - val_accuracy: 0.4668 - val_loss: 1.3380 Epoch 29/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 4s 19ms/step - accuracy: 0.4518 - loss: 1.3800 - val_accuracy: 0.4417 - val_loss: 1.4365 Epoch 30/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 16ms/step - accuracy: 0.4354 - loss: 1.4371 - val_accuracy: 0.4787 - val_loss: 1.3315 Epoch 31/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.4524 - loss: 1.4073 - val_accuracy: 0.4796 - val_loss: 1.3408 Epoch 32/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 16ms/step - accuracy: 0.4521 - loss: 1.3835 - val_accuracy: 0.4929 - val_loss: 1.3004 Epoch 33/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 4s 20ms/step - accuracy: 0.4472 - loss: 1.3721 - val_accuracy: 0.4924 - val_loss: 1.2780 Epoch 34/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 4s 18ms/step - accuracy: 0.4547 - loss: 1.3884 - val_accuracy: 0.4829 - val_loss: 1.3134 Epoch 35/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 16ms/step - accuracy: 0.4336 - loss: 1.4146 - val_accuracy: 0.4853 - val_loss: 1.3357 Epoch 36/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 4s 19ms/step - accuracy: 0.4515 - loss: 1.3929 - val_accuracy: 0.4806 - val_loss: 1.3687 Epoch 37/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 14ms/step - accuracy: 0.4246 - loss: 1.4274 - val_accuracy: 0.4806 - val_loss: 1.3358 Epoch 38/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 13ms/step - accuracy: 0.4472 - loss: 1.3989 - val_accuracy: 0.4962 - val_loss: 1.2797 Epoch 39/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.4433 - loss: 1.3650 - val_accuracy: 0.4853 - val_loss: 1.3399 Epoch 40/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 16ms/step - accuracy: 0.4460 - loss: 1.3705 - val_accuracy: 0.4768 - val_loss: 1.3288 Epoch 41/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 15ms/step - accuracy: 0.4514 - loss: 1.3805 - val_accuracy: 0.4639 - val_loss: 1.3564 Epoch 42/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 15ms/step - accuracy: 0.4707 - loss: 1.3536 - val_accuracy: 0.4934 - val_loss: 1.3075 Epoch 43/50 191/191 ━━━━━━━━━━━━━━━━━━━━ 3s 13ms/step - accuracy: 0.4599 - loss: 1.3647 - val_accuracy: 0.4810 - val_loss: 1.3179
loss_5, acc_5 = modelo_5.evaluate(test_dataset)
print(f"Loss {loss_5:.3}, accuracy {acc_5:.1%}")
loss_list.append(loss_5)
accuracy_list.append(acc_5)
65/65 ━━━━━━━━━━━━━━━━━━━━ 1s 8ms/step - accuracy: 0.5185 - loss: 1.2874 Loss 1.29, accuracy 50.8%
history_dict = history.history
helper.plot_loss_and_accuracy(history_dict['loss'], history_dict['val_loss'], history_dict['accuracy'], history_dict['val_accuracy'])
Red con transferencia del aprendizaje¶

Usando la estrategia "bottleneck" implementa una red que haga transferencia del aprendizaje desde la red VGG16. Si lo haces correctamente, esta red debe obtener mejores resultados que la red que diseñaste en el apartado anterior, con al menos un 80% de acierto sobre el conjunto de test.

Algunos consejos para mejorar tu diseño de red:
- Incluye una o más capas Dense, con sus funciones de activación apropiadas, antes de la capa de salida.
- Intenta usar una capa de tipo GlobalAveragePooling en lugar de la capa Flatten. Esta capa calcula una media de todos los valores de píxeles para cada canal, y en algunas ocasiones produce mejores resultados que la capa Flatten.
- ¡Y no olvides todos los consejos del apartado anterior! También aplican aquí.

Para mejorar aún más los resultados de tu red, utiliza las siguientes ideas:
- Usa las estrategias PRO del ejercicio anterior.
- Prueba otras redes pre-entrenadas de Keras Applications, como Xception o MobileNetV2, o alguna red de las familias EfficientNetV2 o ConvNeXt.
- Usa una estrategia de transfer learning más avanzada, como fine-tuning o una combinación de bottleneck features y fine-tuning. Revisa las diapositivas de clase para saber cómo.
Si empleas todos estos trucos, es posible alcanzar más de un 90% de acierto en el conjunto de test.
Modelo 6: Red con transferencia del aprendizaje¶
En este modelo se está utilizando la red VGG16 pre-entrenada. Al probar el modelo sobre el dataset de 32x32 no se reflejaron mejoras, por lo tanto se decide empezar a variar los argumentos de resolución de las imágenes.
Este modelo tiene:
Capa de Entrada (Lambda)
Capas de Aumento de Datos (RandomFlip, RandomTranslation) para reducir el sobreajuste
Capa VGG16 Base: en este modelo uso la red VGG16 pre-entrenada como base para la extracción de características.
Capa de Pooling Global (GlobalAveragePooling2D)
Capas Densas Superiores: añadidas encima de la capa de pooling global para realizar la clasificación de las características extraídas por la red VGG16.
Capa de Salida (Dense)
Además tiene parada de entrenamiento con EarlyStopping para la métrica de accuracy
Para este modelo se usa una resolucion de 128x128
train_dataset_128 = helper.create_dataset(TRAINDIR, (128,128))
val_dataset_128 = helper.create_dataset(VALDIR, (128,128))
test_dataset_128 = helper.create_dataset(TESTDIR, (128,128))
Found 6082 files belonging to 6 classes. Found 2108 files belonging to 6 classes. Found 2070 files belonging to 6 classes.
train_dataset_256 = helper.create_dataset(TRAINDIR, (256,256))
val_dataset_256 = helper.create_dataset(VALDIR, (256,256))
test_dataset_256 = helper.create_dataset(TESTDIR, (256,256))
Found 6082 files belonging to 6 classes. Found 2108 files belonging to 6 classes. Found 2070 files belonging to 6 classes.
modelo_6 = helper.create_vgg16_transfer_learning_model(input_shape=(128,128, constants.IMAGE_CHANNELS))
c:\Python312\Lib\site-packages\keras\src\layers\core\lambda_layer.py:65: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead. super().__init__(**kwargs)
WARNING:tensorflow:From c:\Python312\Lib\site-packages\keras\src\backend\tensorflow\core.py:184: The name tf.placeholder is deprecated. Please use tf.compat.v1.placeholder instead.
history_6 = modelo_6.fit(train_dataset_128, epochs=5, validation_data=val_dataset_128,
callbacks=[early_stopping])
Epoch 1/5 191/191 ━━━━━━━━━━━━━━━━━━━━ 103s 531ms/step - accuracy: 0.2516 - loss: 1.7847 - val_accuracy: 0.4682 - val_loss: 1.3899 Epoch 2/5 191/191 ━━━━━━━━━━━━━━━━━━━━ 108s 567ms/step - accuracy: 0.4098 - loss: 1.4659 - val_accuracy: 0.5474 - val_loss: 1.1935 Epoch 3/5 191/191 ━━━━━━━━━━━━━━━━━━━━ 108s 564ms/step - accuracy: 0.4736 - loss: 1.3542 - val_accuracy: 0.5731 - val_loss: 1.1468 Epoch 4/5 191/191 ━━━━━━━━━━━━━━━━━━━━ 108s 564ms/step - accuracy: 0.5005 - loss: 1.2954 - val_accuracy: 0.5920 - val_loss: 1.1053 Epoch 5/5 191/191 ━━━━━━━━━━━━━━━━━━━━ 108s 565ms/step - accuracy: 0.5236 - loss: 1.2302 - val_accuracy: 0.6072 - val_loss: 1.0606
loss_6, acc_6 = modelo_6.evaluate(test_dataset_128)
print(f"Loss {loss_6:.3}, accuracy {acc_6:.1%}")
loss_list.append(loss_6)
accuracy_list.append(acc_6)
65/65 ━━━━━━━━━━━━━━━━━━━━ 27s 418ms/step - accuracy: 0.6158 - loss: 1.0152 Loss 1.04, accuracy 60.8%
history_dict_6 = history_6.history
helper.plot_loss_and_accuracy(history_dict_6['loss'], history_dict_6['val_loss'], history_dict_6['accuracy'], history_dict_6['val_accuracy'])
Hay una clara mejora en el accuracy del modelo aumentando la resolución. Con todo fijo, es posible llegar a un accuracy sobre test de al menos 80% aumentando solamente la resolucion? Veamoslo...
Modelo 7: VGG16 con más resolución¶
Es momento de aumentar (aun más) la resolución de las imágenes. Es el mismo modelo anterior pero se le incrementa la resolución.
modelo_7 = helper.create_vgg16_transfer_learning_model(input_shape=(256,256, constants.IMAGE_CHANNELS))
history_7 = modelo_7.fit(train_dataset_256, epochs=5, validation_data=val_dataset_256,
callbacks=[early_stopping])
Epoch 1/5 191/191 ━━━━━━━━━━━━━━━━━━━━ 428s 2s/step - accuracy: 0.2578 - loss: 1.7465 - val_accuracy: 0.5142 - val_loss: 1.3316 Epoch 2/5 191/191 ━━━━━━━━━━━━━━━━━━━━ 427s 2s/step - accuracy: 0.4333 - loss: 1.4357 - val_accuracy: 0.6029 - val_loss: 1.1109 Epoch 3/5 191/191 ━━━━━━━━━━━━━━━━━━━━ 425s 2s/step - accuracy: 0.5145 - loss: 1.2564 - val_accuracy: 0.6428 - val_loss: 0.9895 Epoch 4/5 191/191 ━━━━━━━━━━━━━━━━━━━━ 440s 2s/step - accuracy: 0.5394 - loss: 1.2011 - val_accuracy: 0.6584 - val_loss: 0.9484 Epoch 5/5 191/191 ━━━━━━━━━━━━━━━━━━━━ 427s 2s/step - accuracy: 0.5846 - loss: 1.1386 - val_accuracy: 0.6637 - val_loss: 0.9296
loss_7, acc_7 = modelo_7.evaluate(test_dataset_256)
print(f"Loss {loss_7:.3}, accuracy {acc_7:.1%}")
loss_list.append(loss_7)
accuracy_list.append(acc_7)
65/65 ━━━━━━━━━━━━━━━━━━━━ 108s 2s/step - accuracy: 0.6740 - loss: 0.8825 Loss 0.881, accuracy 66.9%
history_dict_7 = history_7.history
helper.plot_loss_and_accuracy(history_dict_7['loss'], history_dict_7['val_loss'], history_dict_7['accuracy'], history_dict_7['val_accuracy'])
Modelo 8: VGG16 con más epochs¶
Por poco se llega al 80% de accuracy, así que aumentamos el numero de epocas.
modelo_8 = helper.create_vgg16_transfer_learning_model(input_shape=(256,256, constants.IMAGE_CHANNELS))
history_8 = modelo_8.fit(train_dataset_256, epochs=20, validation_data=val_dataset_256,
callbacks=[early_stopping])
Epoch 1/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 426s 2s/step - accuracy: 0.2677 - loss: 1.7528 - val_accuracy: 0.4962 - val_loss: 1.3575 Epoch 2/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 427s 2s/step - accuracy: 0.4433 - loss: 1.4240 - val_accuracy: 0.6015 - val_loss: 1.1169 Epoch 3/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 425s 2s/step - accuracy: 0.5141 - loss: 1.2898 - val_accuracy: 0.6414 - val_loss: 1.0228 Epoch 4/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 425s 2s/step - accuracy: 0.5682 - loss: 1.1636 - val_accuracy: 0.6656 - val_loss: 0.9392 Epoch 5/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 426s 2s/step - accuracy: 0.5760 - loss: 1.1427 - val_accuracy: 0.6679 - val_loss: 0.9202 Epoch 6/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 425s 2s/step - accuracy: 0.6088 - loss: 1.0642 - val_accuracy: 0.6741 - val_loss: 0.9097 Epoch 7/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 425s 2s/step - accuracy: 0.6017 - loss: 1.0671 - val_accuracy: 0.6969 - val_loss: 0.8649 Epoch 8/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 425s 2s/step - accuracy: 0.6306 - loss: 1.0116 - val_accuracy: 0.6912 - val_loss: 0.8654 Epoch 9/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 425s 2s/step - accuracy: 0.6302 - loss: 1.0259 - val_accuracy: 0.6945 - val_loss: 0.8622 Epoch 10/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 425s 2s/step - accuracy: 0.6306 - loss: 1.0082 - val_accuracy: 0.6983 - val_loss: 0.8475 Epoch 11/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 424s 2s/step - accuracy: 0.6457 - loss: 0.9649 - val_accuracy: 0.6926 - val_loss: 0.8540 Epoch 12/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 423s 2s/step - accuracy: 0.6494 - loss: 0.9640 - val_accuracy: 0.7030 - val_loss: 0.8415 Epoch 13/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 423s 2s/step - accuracy: 0.6430 - loss: 0.9685 - val_accuracy: 0.6978 - val_loss: 0.8411 Epoch 14/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 423s 2s/step - accuracy: 0.6472 - loss: 0.9506 - val_accuracy: 0.7073 - val_loss: 0.8244 Epoch 15/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 423s 2s/step - accuracy: 0.6620 - loss: 0.9560 - val_accuracy: 0.7154 - val_loss: 0.8018 Epoch 16/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 421s 2s/step - accuracy: 0.6660 - loss: 0.9090 - val_accuracy: 0.7139 - val_loss: 0.7984 Epoch 17/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 422s 2s/step - accuracy: 0.6790 - loss: 0.9136 - val_accuracy: 0.7139 - val_loss: 0.7968 Epoch 18/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 423s 2s/step - accuracy: 0.6841 - loss: 0.8901 - val_accuracy: 0.7163 - val_loss: 0.7996 Epoch 19/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 425s 2s/step - accuracy: 0.6773 - loss: 0.8749 - val_accuracy: 0.7083 - val_loss: 0.7879 Epoch 20/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 423s 2s/step - accuracy: 0.6615 - loss: 0.9103 - val_accuracy: 0.7211 - val_loss: 0.7717
loss_8, acc_8 = modelo_8.evaluate(test_dataset_256)
print(f"Loss {loss_8:.3}, accuracy {acc_8:.1%}")
loss_list.append(loss_8)
accuracy_list.append(acc_8)
65/65 ━━━━━━━━━━━━━━━━━━━━ 107s 2s/step - accuracy: 0.7412 - loss: 0.7072 Loss 0.711, accuracy 73.4%
history_dict_8 = history_8.history
helper.plot_loss_and_accuracy(history_dict_8['loss'], history_dict_8['val_loss'], history_dict_8['accuracy'], history_dict_8['val_accuracy'])
Mejoras evidentes. Ahora probemos algo más avanzado...
Modelo 9: InceptionResNetV2¶
Este modelo tiene:
Capa de Entrada (Lambda) Preprocesamiento de las imágenes de entrada para adaptarlas al formato esperado por la red InceptionResNetV2.
Capas de Aumento de Datos (RandomFlip, RandomTranslation)
Capa InceptionResNetV2 Base: red InceptionResNetV2 pre-entrenada.
Capa de Pooling Global (GlobalAveragePooling2D): Reduce la dimensionalidad de las características extrayendo características globales de las imágenes.
Capas Densas Superiores:con activación ReLU y regularización Dropout para evitar el sobreajuste.
Capa de Salida (Dense)
Además se aumentan las exigencias: mayior numero de epochs y el dataset mas pesado para la memoria 256x256
modelo_9 = helper.create_inceptionresnetv2_transfer_learning_model()
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/inception_resnet_v2/inception_resnet_v2_weights_tf_dim_ordering_tf_kernels_notop.h5 219055592/219055592 ━━━━━━━━━━━━━━━━━━━━ 21s 0us/step
history_9 = modelo_9.fit(train_dataset_256, epochs=20, validation_data=val_dataset_256,
callbacks=[early_stopping])
Epoch 1/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 284s 1s/step - accuracy: 0.4122 - loss: 1.6547 - val_accuracy: 0.7315 - val_loss: 0.8460 Epoch 2/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 266s 1s/step - accuracy: 0.6209 - loss: 1.0428 - val_accuracy: 0.7661 - val_loss: 0.6995 Epoch 3/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 267s 1s/step - accuracy: 0.6678 - loss: 0.9569 - val_accuracy: 0.8126 - val_loss: 0.6065 Epoch 4/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 267s 1s/step - accuracy: 0.6823 - loss: 0.9145 - val_accuracy: 0.7922 - val_loss: 0.6133 Epoch 5/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 267s 1s/step - accuracy: 0.7115 - loss: 0.8439 - val_accuracy: 0.8131 - val_loss: 0.5788 Epoch 6/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 267s 1s/step - accuracy: 0.7241 - loss: 0.8185 - val_accuracy: 0.8178 - val_loss: 0.5444 Epoch 7/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 268s 1s/step - accuracy: 0.7264 - loss: 0.8063 - val_accuracy: 0.8340 - val_loss: 0.5379 Epoch 8/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 270s 1s/step - accuracy: 0.7348 - loss: 0.7725 - val_accuracy: 0.8212 - val_loss: 0.5459 Epoch 9/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 270s 1s/step - accuracy: 0.7409 - loss: 0.7686 - val_accuracy: 0.8264 - val_loss: 0.5247 Epoch 10/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 275s 1s/step - accuracy: 0.7528 - loss: 0.7256 - val_accuracy: 0.8226 - val_loss: 0.5348 Epoch 11/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 277s 1s/step - accuracy: 0.7468 - loss: 0.7654 - val_accuracy: 0.8269 - val_loss: 0.5189 Epoch 12/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 280s 1s/step - accuracy: 0.7523 - loss: 0.7336 - val_accuracy: 0.8359 - val_loss: 0.5210 Epoch 13/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 275s 1s/step - accuracy: 0.7620 - loss: 0.7304 - val_accuracy: 0.8335 - val_loss: 0.5085 Epoch 14/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 314s 1s/step - accuracy: 0.7509 - loss: 0.7007 - val_accuracy: 0.8368 - val_loss: 0.4915 Epoch 15/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 272s 1s/step - accuracy: 0.7466 - loss: 0.7470 - val_accuracy: 0.8335 - val_loss: 0.4956 Epoch 16/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 267s 1s/step - accuracy: 0.7668 - loss: 0.7019 - val_accuracy: 0.8387 - val_loss: 0.4873 Epoch 17/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 266s 1s/step - accuracy: 0.7710 - loss: 0.6583 - val_accuracy: 0.8401 - val_loss: 0.4866 Epoch 18/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 268s 1s/step - accuracy: 0.7704 - loss: 0.6781 - val_accuracy: 0.8397 - val_loss: 0.4792 Epoch 19/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 266s 1s/step - accuracy: 0.7637 - loss: 0.6852 - val_accuracy: 0.8373 - val_loss: 0.4887 Epoch 20/20 191/191 ━━━━━━━━━━━━━━━━━━━━ 266s 1s/step - accuracy: 0.7786 - loss: 0.6598 - val_accuracy: 0.8534 - val_loss: 0.4790
loss_9, acc_9 = modelo_9.evaluate(test_dataset_256)
print(f"Loss {loss_9:.3}, accuracy {acc_9:.1%}")
loss_list.append(loss_9)
accuracy_list.append(acc_9)
65/65 ━━━━━━━━━━━━━━━━━━━━ 68s 1s/step - accuracy: 0.8455 - loss: 0.4780 Loss 0.465, accuracy 85.1%
history_dict_9 = history_9.history
helper.plot_loss_and_accuracy(history_dict_9['loss'], history_dict_9['val_loss'], history_dict_9['accuracy'], history_dict_9['val_accuracy'])
Informe final y resumen de resultados¶

Escribe en la siguiente celda un pequeño informe con:
- Una tabla de resultados, indicando qué diseños de red has probado y qué resultados en test has obtenido. Puede usar un estilo de tabla como el que se muestra abajo.
- De las estrategias y diseños que has ido probando, ¿qué ha funcionado y qué no?
acc_model= {'Modelo {}'.format(i): locals()['acc_{}'.format(i)] for i in range(1, 10)}
helper.plot_accuracy(acc_model)
Tabla de resumen¶
| Modelo | Procesamiento de imagen | Red Neuronal | Estrategia de Entrenamiento | Datos de Validación | Epochs | Accuracy en Prueba |
|---|---|---|---|---|---|---|
| 1 | Tamaño 32x32, batch size 32 | Conv(4, relu) + Flatten + Dense(6, softmax) | Train from scratch | No | 1 | 28% |
| 2 | Tamaño 32x32, batch size 32 | Conv(4, linear) + Flatten + Dense(6, sigmoid) | Adam optimizer | Sí | 1 | 34% |
| 3 | Tamaño 32x32, batch size 32 | Conv(4, linear) + MaxPooling2D + Flatten + Dense(6, sigmoid) | Adam optimizer | Sí | 10 | 40% |
| 4 | Tamaño 32x32, batch size 32 | Conv(32, relu) + MaxPooling2D + Conv(64, relu) + MaxPooling2D + Conv(128, relu) + MaxPooling2D + Flatten + Dense(256, relu) + Dropout(0.5) + Dense(128, relu) + Dropout(0.5) + Dense(6, softmax) | Adam optimizer | Sí | 10 | 44% |
| 5 | Tamaño 32x32, batch size 32 | Conv(32, relu) + MaxPooling2D + Conv(64, relu) + MaxPooling2D + Conv(128, relu) + MaxPooling2D + Flatten + Dense(256, relu) + Dropout(0.5) + Dense(128, relu) + Dropout(0.5) + Dense(6, softmax) | Adam optimizer + Data Augmentation + Early Stopping | Sí | 50 | 51% |
| 6 | Tamaño 128x128, batch size 32 | VGG16 + Global Average Pooling + Dense(512, relu) + Dropout(0.6) + Dense(128, relu) + Dropout(0.5) + Dense(6, softmax) | Adam optimizer + Early Stopping + Bottleneck | Sí | 5 | 61% |
| 7 | Tamaño 256x256, batch size 32 | VGG16 + Global Average Pooling + Dense(512, relu) + Dropout(0.6) + Dense(128, relu) + Dropout(0.5) + Dense(6, softmax) | Adam optimizer + Early Stopping + Bottleneck | Sí | 5 | 67% |
| 8 | Tamaño 256x256, batch size 32 | VGG16 + Global Average Pooling + Dense(512, relu) + Dropout(0.6) + Dense(128, relu) + Dropout(0.5) + Dense(6, softmax) | Adam optimizer + Early Stopping + Bottleneck | Sí | 20 | 73% |
| 9 | Tamaño 256x256, batch size 32 | InceptionResNetV2 + Global Average Pooling + Dense(512, relu) + Dropout(0.6) + Dense(128, relu) + Dropout(0.5) + Dense(6, softmax) | Adama optimizer + Early Stopping + Bottleneck | Sí | 20 | 85% |
Entrenamiento desde cero vs. Transferencia de aprendizaje: Se observó que el uso de transferencia de aprendizaje, especialmente con redes pre-entrenadas como VGG16 y InceptionResNetV2, produjo un rendimiento superior en comparación con el entrenamiento desde cero. Esto se evidencia en los modelos 6, 7, 8 y 9, que alcanzaron una precisión de prueba más alta que los modelos entrenados desde cero.
Estrategias de regularización: Se implementaron estrategias de regularización, como Dropout, en los modelos 6, 7, 8 y 9 para mitigar el sobreajuste. Estas estrategias ayudaron a mejorar la capacidad de generalización de los modelos y evitar el sobreajuste en los conjuntos de datos de entrenamiento.
Data Augmentation: Se introdujo la técnica de Data Augmentation en el modelo 5 para aumentar la diversidad del conjunto de datos de entrenamiento. Esta técnica resultó en una mejora significativa en la precisión de prueba, lo que demuestra su eficacia para mejorar el rendimiento del modelo en datos no vistos.
Early Stopping: La implementación de Early Stopping en los modelos 5, 6, 7, 8 y 9 contribuyó a evitar el sobreajuste al detener el entrenamiento cuando la pérdida en el conjunto de validación dejó de disminuir. Esto ayudó a mejorar la capacidad de generalización de los modelos y a evitar el sobreajuste. De hecho, cuando se corrio una version del modelo 5 sin introducir Data Augmentation la curva de validation accuracy estaba muuuy por debajo de accuracy training.
Por temas de rendimiento no se aumentaron más el número de epochs ni se jugó mas con el batch_size y el image_size. Sin embargo, el desarrollo incremental demosotró que aumentar el numero de epochs y el tamaño del dataset de entrada puede generar mejoras significativas en el accuracy.
Ahora, basandome en las lecciones aprendidas, hare una afirmacion osada que sería interesante probar dejando el computador procesando todo un finde:
Es posible que ese accurazy sobrepase el 85% facilmente con esta receta:
| Modelo | Procesamiento de imagen | Red Neuronal | Estrategia de Entrenamiento | Datos de Validación | Epochs | Accuracy en Prueba |
|---|---|---|---|---|---|---|
| Hipotetico 10 | Tamaño 256x256, batch size 32 | InceptionResNetV2 + Global Average Pooling + Dense(512, relu) + Dropout(0.6) + Dense(128, relu) + Dropout(0.5) + Dense(6, softmax) | Adam optimizer + Early Stopping + Data Augmentation | Sí | 100 | Más de 85% |
Pero bueno, será un experimento que solo podremos hacer después de sustentar el TFM porque seguro se quedaría la computadora ocupada por 2 días


